package com.tucc.hello.juc;

import cn.hutool.core.thread.RejectPolicy;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author tucc
 * @description todo
 * @package com.tucc.hello.juc
 * @date 2021/5/26
 **/
public class ProducerConsumer {

    ReentrantLock lock = new ReentrantLock();

    Condition notEmpty = lock.newCondition();

    Condition notFull = lock.newCondition();

    int maxSize;

    volatile int size;

    volatile List<Object> list;

    public void add(Object o) {
        lock.lock();
        try {
            while (size == maxSize) {
                System.out.println("now is full");
                notFull.await();
            }
            list.add(o);
            size = size + 1;
            notEmpty.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public Object get() {
        lock.lock();
        try {
            while (size == 0) {
                System.out.println("now is Empty");
                notEmpty.await();
            }
            Object obj = list.remove(0);
            size = size - 1;
            notFull.signal();
            return obj;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }

    ProducerConsumer(int maxSize) {
        this.maxSize = maxSize;
        size = 0;
        list = new LinkedList<>();
    }

    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10);
        ProducerConsumer producerConsumer = new ProducerConsumer(2);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10,
                1L, TimeUnit.MINUTES, queue, RejectPolicy.DISCARD.getValue());
        threadPoolExecutor.submit(new Producer(producerConsumer, 50));
        threadPoolExecutor.submit(new Consumer(producerConsumer, 100));
    }

    static class Producer implements Runnable {

        ProducerConsumer producerConsumer;

        long rate;

        Producer(ProducerConsumer producerConsumer, long rate){
            this.producerConsumer = producerConsumer;
            this.rate = rate;
        }

        @Override
        public void run() {
            int i = 0;
            while(i ++ < 10){
                producerConsumer.add(i);
                System.out.println("producer add " + i);
                try {
                    TimeUnit.MILLISECONDS.sleep(rate);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Consumer implements Runnable {

        ProducerConsumer producerConsumer;

        long rate;

        Consumer(ProducerConsumer producerConsumer, long rate){
            this.producerConsumer = producerConsumer;
            this.rate = rate;
        }

        @Override
        public void run() {
            int i = 0;
            while(i ++ < 10){
                Object o = producerConsumer.get();
                System.out.println("consumer get " + o);
                try {
                    TimeUnit.MILLISECONDS.sleep(rate);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
